﻿using System;
using System.Runtime.CompilerServices;

namespace Net
{
    [Serializable]
    public struct Rect : IEquatable<Rect>
    {
#if UNITY_EDITOR
        [UnityEngine.SerializeField]
        private float m_XMin;
        [UnityEngine.SerializeField]
        private float m_YMin;
        [UnityEngine.SerializeField]
        private float m_Width;
        [UnityEngine.SerializeField]
        private float m_Height;
#else
        private float m_XMin;
        private float m_YMin;
        private float m_Width;
        private float m_Height;
#endif

        [Obsolete("use xMin")]
        public float left => m_XMin;

        [Obsolete("use xMax")]
        public float right => m_XMin + m_Width;

        [Obsolete("use yMin")]
        public float top => m_YMin;

        [Obsolete("use yMax")]
        public float bottom => m_YMin + m_Height;

        public static Rect zero => new Rect(0f, 0f, 0f, 0f);

        public float x
        {
            get => m_XMin;
            set => m_XMin = value;
        }

        public float y
        {
            get => m_YMin;
            set => m_YMin = value;
        }

        public Vector2 position
        {
            get => new Vector2(m_XMin, m_YMin);
            set
            {
                m_XMin = value.x;
                m_YMin = value.y;
            }
        }

        public Vector2 center
        {
            get => new Vector2(x + m_Width / 2f, y + m_Height / 2f);
            set
            {
                m_XMin = value.x - m_Width / 2f;
                m_YMin = value.y - m_Height / 2f;
            }
        }

        public Vector2 min
        {
            get => new Vector2(xMin, yMin);
            set
            {
                xMin = value.x;
                yMin = value.y;
            }
        }

        public Vector2 max
        {
            get => new Vector2(xMax, yMax);
            set
            {
                xMax = value.x;
                yMax = value.y;
            }
        }

        public float width
        {
            get => m_Width;
            set => m_Width = value;
        }

        public float height
        {
            get => m_Height;
            set => m_Height = value;
        }

        public Vector2 size
        {
            get => new Vector2(m_Width, m_Height);
            set
            {
                m_Width = value.x;
                m_Height = value.y;
            }
        }

        public float xMin
        {
            get => m_XMin;
            set
            {
                float xMax = this.xMax;
                m_XMin = value;
                m_Width = xMax - m_XMin;
            }
        }

        public float yMin
        {
            get => m_YMin;
            set
            {
                float yMax = this.yMax;
                m_YMin = value;
                m_Height = yMax - m_YMin;
            }
        }

        public float xMax
        {
            get => m_Width + m_XMin;
            set => m_Width = value - m_XMin;
        }

        public float yMax
        {
            get => m_Height + m_YMin;
            set => m_Height = value - m_YMin;
        }

        #region 源码
        public Rect(float x, float y, float width, float height)
        {
            m_XMin = x;
            m_YMin = y;
            m_Width = width;
            m_Height = height;
        }

        public Rect(Vector2 position, Vector2 size)
        {
            m_XMin = position.x;
            m_YMin = position.y;
            m_Width = size.x;
            m_Height = size.y;
        }

        public Rect(Rect source)
        {
            m_XMin = source.m_XMin;
            m_YMin = source.m_YMin;
            m_Width = source.m_Width;
            m_Height = source.m_Height;
        }

        public static Rect MinMaxRect(float xmin, float ymin, float xmax, float ymax)
        {
            return new Rect(xmin, ymin, xmax - xmin, ymax - ymin);
        }

        public void Set(float x, float y, float width, float height)
        {
            m_XMin = x;
            m_YMin = y;
            m_Width = width;
            m_Height = height;
        }

        public bool Contains(Vector2 point)
        {
            return point.x >= xMin && point.x < xMax && point.y >= yMin && point.y < yMax;
        }

        public bool Contains(Vector3 point)
        {
            return point.x >= xMin && point.x < xMax && point.y >= yMin && point.y < yMax;
        }

        public bool ContainsXZ(Vector3 point)
        {
            return point.x >= xMin && point.x < xMax && point.z >= yMin && point.z < yMax;
        }

        public bool Contains(Vector3 point, bool allowInverse)
        {
            bool result;
            if (!allowInverse)
            {
                result = Contains(point);
            }
            else
            {
                bool flag = false;
                if ((width < 0f && point.x <= xMin && point.x > xMax) || (width >= 0f && point.x >= xMin && point.x < xMax))
                {
                    flag = true;
                }
                result = (flag && ((height < 0f && point.y <= yMin && point.y > yMax) || (height >= 0f && point.y >= yMin && point.y < yMax)));
            }
            return result;
        }

        private static Rect OrderMinMax(Rect rect)
        {
            if (rect.xMin > rect.xMax)
            {
                float xMin = rect.xMin;
                rect.xMin = rect.xMax;
                rect.xMax = xMin;
            }
            if (rect.yMin > rect.yMax)
            {
                float yMin = rect.yMin;
                rect.yMin = rect.yMax;
                rect.yMax = yMin;
            }
            return rect;
        }

        public bool Overlaps(Rect other)
        {
            return other.xMax > xMin && other.xMin < xMax && other.yMax > yMin && other.yMin < yMax;
        }

        public bool Overlaps(Rect other, bool allowInverse)
        {
            Rect rect = this;
            if (allowInverse)
            {
                rect = Rect.OrderMinMax(rect);
                other = Rect.OrderMinMax(other);
            }
            return rect.Overlaps(other);
        }

        public static Vector2 NormalizedToPoint(Rect rectangle, Vector2 normalizedRectCoordinates)
        {
            return new Vector2(Mathf.Lerp(rectangle.x, rectangle.xMax, normalizedRectCoordinates.x), Mathf.Lerp(rectangle.y, rectangle.yMax, normalizedRectCoordinates.y));
        }

        public static Vector2 PointToNormalized(Rect rectangle, Vector2 point)
        {
            return new Vector2(Mathf.InverseLerp(rectangle.x, rectangle.xMax, point.x), Mathf.InverseLerp(rectangle.y, rectangle.yMax, point.y));
        }

        public static bool operator !=(Rect lhs, Rect rhs)
        {
            return !(lhs == rhs);
        }

        public static bool operator ==(Rect lhs, Rect rhs)
        {
            return lhs.x == rhs.x && lhs.y == rhs.y && lhs.width == rhs.width && lhs.height == rhs.height;
        }

        public override int GetHashCode()
        {
            return x.GetHashCode() ^ width.GetHashCode() << 2 ^ y.GetHashCode() >> 2 ^ height.GetHashCode() >> 1;
        }

        public override bool Equals(object other)
        {
            return other is Rect && Equals((Rect)other);
        }

        public bool Equals(Rect other)
        {
            return x.Equals(other.x) && y.Equals(other.y) && width.Equals(other.width) && height.Equals(other.height);
        }

        public override string ToString()
        {
            return string.Format("(x:{0:F2}, y:{1:F2}, width:{2:F2}, height:{3:F2})", new object[]
            {
                x,
                y,
                width,
                height
            });
        }

        public string ToString(string format)
        {
            return string.Format("(x:{0}, y:{1}, width:{2}, height:{3})", new object[]
            {
                x.ToString(format),
                y.ToString(format),
                width.ToString(format),
                height.ToString(format)
            });
        }
        #endregion

        public static UnityEngine.Rect operator +(UnityEngine.Rect a, Rect b)
        {
            return new UnityEngine.Rect(a.x + b.x, a.y + b.y, a.width + b.width, a.height + b.height);
        }

        public static UnityEngine.Rect operator -(UnityEngine.Rect a, Rect b)
        {
            return new UnityEngine.Rect(a.x - b.x, a.y - b.y, a.width - b.width, a.height - b.height);
        }

        public static UnityEngine.Rect operator *(UnityEngine.Rect a, Rect b)
        {
            return new UnityEngine.Rect(a.x * b.x, a.y * b.y, a.width * b.width, a.height * b.height);
        }

        public static UnityEngine.Rect operator /(UnityEngine.Rect a, Rect b)
        {
            return new UnityEngine.Rect(a.x / b.x, a.y / b.y, a.width / b.width, a.height / b.height);
        }

        public static Rect operator +(Rect a, UnityEngine.Rect b)
        {
            return new Rect(a.x + b.x, a.y + b.y, a.width + b.width, a.height + b.height);
        }

        public static Rect operator -(Rect a, UnityEngine.Rect b)
        {
            return new Rect(a.x - b.x, a.y - b.y, a.width - b.width, a.height - b.height);
        }

        public static Rect operator *(Rect a, UnityEngine.Rect b)
        {
            return new Rect(a.x * b.x, a.y * b.y, a.width * b.width, a.height * b.height);
        }

        public static Rect operator /(Rect a, UnityEngine.Rect b)
        {
            return new Rect(a.x / b.x, a.y / b.y, a.width / b.width, a.height / b.height);
        }

        public static bool operator ==(UnityEngine.Rect lhs, Rect rhs)
        {
            return lhs.x == rhs.x && lhs.y == rhs.y && lhs.width == rhs.width && lhs.height == rhs.height;
        }

        public static bool operator !=(UnityEngine.Rect lhs, Rect rhs)
        {
            return !(lhs == rhs);
        }

        public static implicit operator UnityEngine.Rect(Rect v)
        {
            return new UnityEngine.Rect(v.x, v.y, v.width, v.height);
        }

        public static implicit operator Rect(UnityEngine.Rect v)
        {
            return new Rect(v.x, v.y, v.width, v.height);
        }
    }
}